<?php
/**
 * RedXenon Framework
 * 
 * LICENSE
 * 
 * This source file is subject to the New BSD license that is bundled
 * with this package in the file LICENSE.txt
 * A copy of this license can be found in the world-wide-web at this URL:
 * http://www.redxenon.com/license/new-bsd-license.txt
 */

/** File Object */
require_once "RXFile.php";

/**
 * RedXenon File (RXFileManager.php)
 * Defines some methods to be applied to files
 * 
 * @category RedXenon
 * @package RedXenon_File
 * @copyright Copyright (c) 2011 Red Xenon (http://www.redxenon.com)
 * @version $Id: RXFileManager.php 1.0 2011-04-29 $ 
 */
class RXFileManager {	
	
	/**
	 * File object
	 * @var RXFile
	 */
	protected $file;
	
	/**
	 * Resource handler to the managed file
	 * @var Resource
	 */
	protected $fileHandler;
	
	/**
	 * Opens a File
	 * @param String that defines the mode to open the file, if null this mode is Read Only
	 */
	public function open($mode = null){
		if($this->fileHandler){
			throw new Exception("The file \"".$this->file->getPath()."\" is already open" . PHP_EOL);
		}
			
		
		if($this->file->getPath()){
			$mode = strtolower($mode);
			
			/* If any mode is enabled, Mode is ReadOnly */
			if(!$mode){
				$mode = "r";
			}
			
			/* Check to ensure that the file is readable or writeable */
			switch($mode){
				case "r"		:	if(!is_readable($this->file->getPath()) && file_exists($this->file->getPath())){
											throw new Exception("The file \"".$this->file->getPath()."\" is not readable" . PHP_EOL);		
										}
										break;
				case "w"		:
				case "a"		:
				case "c"		:	if(!is_writable($this->file->getPath()) && file_exists($this->file->getPath())){
											throw new Exception("The file \"".$this->file->getPath()."\" is not writeble" . PHP_EOL);		
										}
										break;
										
				case "r+"	:
				case "w+"	:
				case "a+"	:
				case "c+"	:	
										if((!is_readable($this->file->getPath() || !is_writable($this->file->getPath()) && file_exists($this->file->getPath())))){
											throw new Exception("The file \"".$this->file->getPath()."\" is not readable or writeable "
											. PHP_EOL);
										}
										break;
				default		:	throw new Exception("The operation mode to open the file \"".$this->file->getPath()."\"
																				is not applicable" . PHP_EOL);
			}
			
			/* Finally open the file */
			$this->fileHandler = @fopen($this->file->getPath(), $mode);
			if(!$this->fileHandler){
				throw new Exception("The file \"".$this->file->getPath()."\" cannot be opened" . PHP_EOL);
			}
			
		}else{
			throw new Exception("The file \"".$this->file->getPath()."\" does not exist and cannot be opened" . PHP_EOL);
		}
	}
	
	/**
	 * Closes the file in case it is not closed at the end of the execution
	 */
	public function __destruct(){
	}
	
	/**
	 * Closes a File
	 */
	public function close(){
		if($this->fileHandler){
			$status = fclose($this->fileHandler);
			if(!$status){
				throw new Exception("The file \"".$this->file->getPath()."\" cannot be closed" . PHP_EOL);
			}
			unset($this->fileHandler);
		}else{
			throw new Exception("The file \"".$this->file->getPath()."\" is already closed" . PHP_EOL);
		}
	}
	
	/**
	 * Deletes the file
	 * @return void | Exception in case of file deletion protection
	 */
	public function delete(){
		if($this->fileHandler){
			self::close();
			unset($this->fileHandler);
		}
		
		if(!unlink($this->file->getPath())){
			throw new Exception("The file \"".$this->file->getPath()."\" cannot be deleted" . PHP_EOL);
		}
	}
	
	/**
	 * Tries to change the File permissions
	 * @param Integer $permissions
	 * @return TRUE | FALSE
	 */
	public function changePermissions($permissions){
		if(chmod($this->file->getPath(), $permissions)){
			$this->file->update();
			return TRUE;
		}else{
			return FALSE;
		}
	}
	
	/**
	 * Renames a file (only its name)
	 * @param String $name
	 */
	public function rename($name){
		if(rename($this->file->getPath(), $this->file->getParentDirectory()."/".$name)){
			$this->file->setPath($this->file->getParentDirectory()."/".$name);
			$this->file->update();
			return TRUE;
		}else{
			return FALSE;
		}
	}
	
	/**
	 * Moves a file from its directory to another preserving its original name
	 * @param String $newPath (Directory to move it, not the full path to the file)
	 */
	public function move($newPath){
		if($newPath[strlen($newPath) - 1] != "/"){
			$newPath = $newPath."/";
		}
		if(rename($this->file->getPath(), $newPath.$this->file->getBaseName())){
			$this->file->setPath(newPath.$this->file->getBaseName());
			$this->file->update();
			return TRUE;
		}else{
			return FALSE;
		}
	}
	
	/**
	 * @return The file that is being managed
	 */
	public function getFile() {
		return $this->file;
	}

	/**
	 * @param File object to manage
	 */
	public function setFile($file) {
		$this->file = $file;
	}
	
	/**
	 * Sets the File Object to a new Object
	 * @param String $filePath
	 */
	public function setNewFile($filePath){
		$this->file = new RXFile($filePath);
	}
	
	/**
	 * Writes a content in a file
	 * If file is not open, function named open() is called
	 * @param String $content
	 */
	public function write($content){
		if(!$this->fileHandler){
			/* If file is closed, trying to open it */
			self::open("a+");
		}
		if($this->fileHandler){
			/* We lock the file */
			if(flock($this->fileHandler, LOCK_EX)){
				/* In case that we are sure it has been opened we try to write */
				if(is_numeric(@fwrite($this->fileHandler, $content))){
					/* Write is done and everything went ok, closing and updating */					
					$this->file->update();
				}else{					
					throw new Exception("File \"".$this->file->getPath()."\" could not be written, an error ocurred");
				}
				flock($this->fileHandler, LOCK_UN);
			}
			self::close();
		}else{
			throw new Exception("File \"".$this->file->getPath()."\" could not be written");
		}
	}
	
	/**
	 * Reads a file and stores its content in a String variable
	 * The buffer can be filled with a limited number of bytes
	 * @param Integer $bytes to read
	 * @return String
	 */
	public function read($bytes = null){
		if(!$this->fileHandler){
			/* If file is close, trying to open it */
			self::open();
		}
		
		if($this->fileHandler){
			if(!is_null($bytes) && $bytes > 0){
				$content = @fread($this->fileHandler, $bytes);
			}else{
				$content = @fread($this->fileHandler, $this->file->getSize());
			}
			
			if($content !== FALSE){
				self::close();
				return $content;
			}else{
				self::close();
				throw new Exception("File \"".$this->file->getPath()."\" could not be read");
			}
		}else{
			throw new Exception("File \"".$this->file->getPath()."\" could not be read");
		}
	}
	
	/**
	 * Reads a file and returns its content splitted in lines
	 * @return Array of lines | FALSE in case of error
	 */
	public function splitFileInLines(){
		try{
			$content = self::read();
			return explode("\n", $content);
		}catch(Exception $e){			
			throw $e;
			return FALSE;
		}
	}
}

?>